package core.lib.network;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Hashtable;
import java.util.LinkedList;

import core.lib.CoreConstants;
import core.lib.CoreEventDispatcher;
import core.lib.CoreRequestDispatcher;
import core.lib.interfaces.HttpAgent;
import core.lib.interfaces.ProtocolThreadManager;
import core.lib.interfaces.SocketAgent;
import core.lib.model.CoreRequest;
import core.lib.model.Module;

public class NetworkModule extends Module implements ProtocolThreadManager
{
	private final LinkedList<BaseProtocolThread> mProtocolThreads;
	private final Hashtable<BaseProtocolRequest, CoreRequest> mDispatchedProtocolRequests;

	private SocketAgent mSocketAgent;
	private HttpAgent mHttpAgent;

	public NetworkModule(CoreRequestDispatcher crd, CoreEventDispatcher ced)
	{
		super(crd, ced);
		mProtocolThreads = new LinkedList<>();
		mDispatchedProtocolRequests = new Hashtable<>();
		crd.registerCoreRequestHandler("network", this);
	}

	@Override
	protected byte doDestroy()
	{
		return 0;
	}

	@Override
	protected void doHandleRequest(CoreRequest req)
	{
		URI targetURI = req.getURI();

		if (NetworkConstants.Method.SET_SOCKET_AGENT.equals(targetURI.getPath().toLowerCase()))
		{
			SocketAgent agent = (SocketAgent) req.getParameter(NetworkConstants.Parameters.AGENT_CLASS);
			setSocketAgent(agent);
		}
		else if (NetworkConstants.Method.SET_HTTP_AGENT.equals(targetURI.getPath().toLowerCase()))
		{
			HttpAgent agent = (HttpAgent) req.getParameter(NetworkConstants.Parameters.AGENT_CLASS);
			setHttpAgent(agent);
		}
		else if (NetworkConstants.Method.SEND_SEVER_MSG.equals(targetURI.getPath().toLowerCase()))
		{
			BaseProtocolContext context = (BaseProtocolContext) req.getParameter(NetworkConstants.Parameters.CONTEXT);
			if (context == null)
			{
				req.setResponse(CoreConstants.RequestReturnCode.FATAL_ERROR, "Context can not be null!");
			}
			else
			{
				BaseProtocolRequest preq = (BaseProtocolRequest) req.getParameter(NetworkConstants.Parameters.PROTOCOL_REQUEST);
				if (preq == null)
				{
					req.setResponse(CoreConstants.RequestReturnCode.FATAL_ERROR, "ProtocolRequest can not be null!");
				}
				else
				{
					deal(context, req, preq);
				}
			}
		}
		else
		{
			throw new IllegalArgumentException("Unrecognized req type!");
		}

	}

	public void deal(BaseProtocolContext context, CoreRequest req, BaseProtocolRequest pr)
	{
		BaseProtocolThread targetThread = null;

		synchronized (mProtocolThreads)
		{
			for (BaseProtocolThread t : mProtocolThreads)
			{
				if (t.getContext().equals(context))
				{
					if (t.getThreadState() == BaseProtocolThread.THREAD_STATE_OFF)
					{
						mProtocolThreads.remove(t);
						continue;
					}
					else
					{
						targetThread = t;
						break;
					}
				}
			}

			try
			{
				Constructor<? extends BaseProtocolThread> c = context.getProtocolThreadClass().getDeclaredConstructor(ProtocolThreadManager.class, context.getClass());
				targetThread = c.newInstance(this, context);
				mProtocolThreads.add(targetThread);
			}
			catch (NoSuchMethodException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (SecurityException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (InstantiationException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IllegalAccessException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IllegalArgumentException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (InvocationTargetException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

		if (targetThread != null)
		{
			mDispatchedProtocolRequests.put(pr, req);
			targetThread.handleProtocolRequest(pr);
		}
	}

	public void setSocketAgent(SocketAgent socketAgent)
	{
		mSocketAgent = socketAgent;
	}

	public void setHttpAgent(HttpAgent httpAgent)
	{
		mHttpAgent = httpAgent;
	}

	@Override
	public HttpAgent getHttpAgent()
	{
		return mHttpAgent;
	}

	@Override
	public SocketAgent getSocketAgent()
	{
		return mSocketAgent;
	}

	@Override
	public void onThreadStateChange()
	{
		// TODO Auto-generated method stub
	}

	@Override
	public void onProtocolRequestFinished(BaseProtocolRequest pr)
	{
		CoreRequest req = mDispatchedProtocolRequests.remove(pr);
		if (req != null)
		{
			req.setResponse(CoreConstants.RequestReturnCode.OK, null);
		}
	}

	@Override
	public void onProtocolRequestFailed(BaseProtocolRequest pr)
	{
		CoreRequest req = mDispatchedProtocolRequests.remove(pr);
		if (req != null)
		{
			req.setResponse(CoreConstants.RequestReturnCode.NETWORK_FAILURE, null);
		}
	}
}
